package net.entelijan.cobean.data.literature.inmem;

import java.beans.XMLDecoder;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.ejb.Remote;
import javax.ejb.Stateless;

import net.entelijan.cobean.data.literature.ILitService;
import net.entelijan.cobean.data.literature.LitAuthor;
import net.entelijan.cobean.data.literature.LitBook;
import net.entelijan.cobean.data.literature.LitBookSearchItem;
import net.entelijan.cobean.data.literature.OrderAttribute;
import net.entelijan.cobean.data.util.io.IInputStreamUser;
import net.entelijan.cobean.data.util.io.ResourceReader;

@Stateless
@Remote(ILitService.class)
public class InMemLitService implements ILitService {

	private String resource = "lit.xml";

	private LitContainer container = null;

	// ID for creating new Books or authors
	private long nextID = 100000;

	public InMemLitService() {
		super();
	}

	@Override
	public LitAuthor loadAuthor(long id) {
		LitAuthor re = findAuthor(id);
		if (re != null) {
			re = createDeepCopy(re);
		}
		return re;
	}

	private LitAuthor findAuthor(long id) {
		LitAuthor re = null;
		LitContainer cont = getContainer();
		for (LitAuthor author : cont.getAuthors()) {
			if (author.getId() == id) {
				re = author;
				break;
			}
		}
		return re;
	}

	@Override
	public LitBook loadBook(long id) {
		LitBook re = findBook(id);
		if (re != null) {
			re = createDeepCopy(re);
		}
		return re;
	}

	private LitBook findBook(long id) {
		LitBook re = null;
		LitContainer cont = getContainer();
		for (LitBook book : cont.getBooks()) {
			if (book.getId() == id) {
				re = book;
				break;
			}
		}
		return re;
	}

	@Override
	public void saveAuthor(LitAuthor author) {
		LitAuthor old = findAuthor(author.getId());
		if (old == null) {
			long newId = this.nextID++;
			author.setId(newId);
			getContainer().getAuthors().add(author);
		} else {
			flatCopyProperties(author, old);
		}
	}

	@Override
	public void saveBook(LitBook book) {
		if (book.getId() < 0) {
			long newId = this.nextID++;
			book.setId(newId);
			getContainer().getBooks().add(book);
		} else {
			LitBook old = findBook(book.getId());
			flatCopyProperties(book, old);
		}
	}

	@Override
	public List<LitAuthor> loadAllAuthors() {
		LitContainer c = getContainer();
		return c.getAuthors();
	}

	@Override
	public List<LitBook> loadAllBooks() {
		LitContainer c = getContainer();
		return c.getBooks();
	}

	@Override
	public void addBookToAuthor(LitAuthor author, LitBook book) {
		LitBook oldBook = findBook(book.getId());
		if (oldBook == null) {
			throw new IllegalStateException(
					"Cannot add abook that was not saved before. " + book);
		}
		if (oldBook.getAuthor() != null) {
			throw new IllegalStateException(
					"Cannot add book to author because it is already added to another author. "
							+ oldBook);
		}
		LitAuthor oldAuthor = findAuthor(author.getId());
		if (oldAuthor == null) {
			throw new IllegalStateException(
					"Cannot add a book to an author that is not yet saved. "
							+ oldAuthor);
		}
		if (oldAuthor.getBooks() == null) {
			List<LitBook> books = new ArrayList<LitBook>();
			books.add(oldBook);
			oldAuthor.setBooks(books);
		} else {
			oldAuthor.getBooks().add(oldBook);
		}
		oldBook.setAuthor(oldAuthor);
	}

	@Override
	public void removeBookFromAuthor(LitAuthor author, LitBook book) {
		LitAuthor oldAuthor = findAuthor(author.getId());
		if (oldAuthor != null && oldAuthor.getBooks() != null) {
			for (LitBook b : oldAuthor.getBooks()) {
				if (b.getId() == book.getId()) {
					oldAuthor.getBooks().remove(b);
					break;
				}
			}
		}
	}

	private LitAuthor createDeepCopy(LitAuthor author) {
		LitAuthor re = createFlatCopy(author);
		if (author.getBooks() != null) {
			List<LitBook> reBooks = new ArrayList<LitBook>();
			for (LitBook b : author.getBooks()) {
				LitBook b1 = createFlatCopy(b);
				reBooks.add(b1);
				b1.setAuthor(re);
			}
			re.setBooks(reBooks);
		}
		return re;
	}

	private LitBook createDeepCopy(LitBook book) {
		LitBook re = createFlatCopy(book);
		if (book.getAuthor() != null) {
			re.setAuthor(createFlatCopy(book.getAuthor()));
		}
		return re;
	}

	private LitBook createFlatCopy(LitBook b) {
		LitBook re = new LitBook();
		flatCopyProperties(b, re);
		return re;
	}

	private void flatCopyProperties(LitBook from, LitBook to) {
		to.setId(from.getId());
		to.setSubtitle(from.getSubtitle());
		to.setTitle(from.getTitle());
		to.setTranslation(from.getTranslation());
		to.setYear(from.getYear());
	}

	private LitAuthor createFlatCopy(LitAuthor a) {
		LitAuthor re = new LitAuthor();
		flatCopyProperties(a, re);
		return re;
	}

	private void flatCopyProperties(LitAuthor from, LitAuthor to) {
		to.setId(from.getId());
		to.setBirthday(from.getBirthday());
		to.setCountry(from.getCountry());
		to.setLanguage(from.getLanguage());
		to.setName(from.getName());
	}

	private LitContainer getContainer() {
		if (this.container == null) {
			container = createLitContainer();
		}
		return this.container;
	}

	private LitContainer createLitContainer() {
		final LitContainer re = new LitContainer();
		ResourceReader fr = new ResourceReader();
		fr.read(this.resource, new IInputStreamUser() {

			@SuppressWarnings("unchecked")
			@Override
			public void use(InputStream in) {
				XMLDecoder dec = new XMLDecoder(in);
				re.setBooks((List<LitBook>) dec.readObject());
				re.setAuthors((List<LitAuthor>) dec.readObject());
			}
		});

		return re;

	}

	public void setResource(String res) {
		this.resource = res;
	}

	public String getResource() {
		return resource;
	}

	private static class LitContainer {

		private List<LitBook> books;
		private List<LitAuthor> authors;

		public List<LitBook> getBooks() {
			return books;
		}

		public void setBooks(List<LitBook> books) {
			this.books = books;
		}

		public List<LitAuthor> getAuthors() {
			return authors;
		}

		public void setAuthors(List<LitAuthor> authors) {
			this.authors = authors;
		}

	}

	@Override
	public List<LitBook> findBooks(LitBookSearchItem searchItem) {
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public long getBookCount() {
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public List<LitBook> loadBooks(int fromIndex, int toIndex,
			List<OrderAttribute> order) {
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public void deleteAuthor(long id) {
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public void deleteBook(long id) {
		throw new UnsupportedOperationException("Not yet implemented");
	}

}
